/*
 * Amiga MUD
 *
 * Copyright (c) 1995 by Chris Gray
 */

/*
 * news.m - allow the player to interact with usenet news.
 */

/* NOTE: this current setup is intended for use with Matt Dillon's UUCP
   and news distribution, version 1.16 or later. To use it with other
   forms of news, you will perhaps need to change various things.
   Dependencies: UULIB:NewsGroups, a hierarchical set of news directories
   in UUNEWS:, postnews, T: */

use t_streets

/* The following property is actually added to SysAdmin. It is used as a
   global variable to check whether this MUD allows players to set their
   own realnames. Since mail/news can go all over the world, many sysadmins
   may prefer that everything that originates with their site at least have
   a valid and traceable name attached to it. Set the value to 'true' if
   you want people to be able to change their own realnames. Think seriously
   about it before you do it. */

define tp_news p_pCanChangeName CreateBoolProp().
CharacterThing(Character("SysAdmin"))@p_pCanChangeName := false.

define tp_news NewsThing CreateThing(nil).
define tp_news p_vNewsParse CreateActionProp().
define tp_news p_vNewsGroupParse CreateActionProp().
define tp_news p_vNewsHeaderParse CreateActionProp().
define tp_news p_vNewsBodyParse CreateActionProp().

define tp_news g_news CreateGrammar().

define tp_news p_pNRealName CreateStringProp().
define tp_news p_pNSaveHandler CreateActionProp().
define tp_news p_pNSaveIdleHandler CreateActionProp().
define tp_news p_pNSavePrompt CreateStringProp().
define tp_news p_pNGroupList CreateThingListProp().
define tp_news p_pNCurrentGroup CreateIntProp().
define tp_news p_pNUnreadIndex CreateIntProp().
define tp_news p_pNCurrentArticle CreateIntProp().
define tp_news p_pNFd CreateIntProp().
define tp_news p_pNString CreateStringProp().
define tp_news p_pNString2 CreateStringProp().
define tp_news p_pNSubject CreateStringProp().
define tp_news p_pNEndAction CreateActionProp().

define tp_news p_NGroupName CreateStringProp().
define tp_news p_NHighestRead CreateIntProp().
define tp_news p_NLowest CreateIntProp().
define tp_news p_NHighest CreateIntProp().
define tp_news p_NUnread CreateIntListProp().

CharacterThing(SysAdmin)@p_pNRealName := "MUD administrator".

define tp_news proc nv_help()bool:
    Print(
"Commands in the newsroom are:\n\n"
"  bye/quit - exit from the program\n"
"  exit/leave/out - leave the newsroom\n"
"  info - some additional comments on MUD news\n"
"  groups/newsgroups - print the available groups\n"
"  subscribe/sub - print the groups you are subscribed to\n"
"  subscribe/sub \"group\" - subscribe to the given group\n"
"  unsubscribe/unsub \"group\" - unsubscribe from the given group\n"
"  catchup \"group\" - mark all current articles as read\n"
"  read - start reading news\n"
"  name/realname <name> - set your real name to <name>\n"
"  post \"group\" [subject] - post an article to the given group\n"
    );
    true
corp;

Verb0(g_news, "help", 0, nv_help).
Synonym(g_news, "help", "?").

define tp_news proc nv_info()bool:

    Print(
"Newsgroup names almost always contain periods, so you must enclose them in "
"quotes to prevent MUD's normal parsing from treating e.g.\n"
"    subscribe rec.games.mud\n"
"as 'subscribe rec' followed by 'games' followed by 'mud'. The optional "
"subject on the 'post' command can be quoted or not, depending on whether "
"you need to put punctuation in it. Similarly, your real name on the 'name' "
"command can be quoted or not. The MUD newsreader is quite primitive - it "
"doesn't understand message threads, followups, replies, etc. If you need to "
"use usenet news a lot, you should try to get a normal usenet connection.\n"
    );
    true
corp;

Verb0(g_news, "info", 0, nv_info).

define tp_news proc nv_groups()bool:
    int fd, blank;
    string line;
    bool first;

    fd := FileOpenForRead("UULIB:NewsGroups");
    if fd = 0 then
	Print("This MUD is not set up to offer news reading.\n");
    else
	Print("Newsgroups available on this machine:\n");
	first := true;
	while
	    line := FileRead(fd);
	    line ~= ""
	do
	    blank := Index(line, " ");
	    if blank < 0 then
		blank := Index(line, "\t");
	    fi;
	    if blank > 0 then
		line := SubString(line, 0, blank);
	    fi;
	    if not line == "Junk" then
		if first then
		    first := false;
		else
		    Print(", ");
		fi;
		Print(line);
	    fi;
	od;
	Print("\n");
	FileClose(fd);
    fi;
    true
corp;

Verb0(g_news, "groups", 0, nv_groups).
Synonym(g_news, "groups", "group");
Synonym(g_news, "groups", "newsgroups").

define tp_news proc nv_subscribe(string group)bool:
    thing me, th;
    int count, n, fd;
    list thing lt;
    string line;

    me := Me();
    lt := me@p_pNGroupList;
    if group = "" then
	if lt = nil or Count(lt) = 0 then
	    Print("You are not currently subscribed to any groups.\n");
	else
	    Print("You are currently subscribed to the following groups:\n");
	    count := Count(lt);
	    n := 0;
	    while n ~= count do
		Print("  ");
		Print(lt[n]@p_NGroupName);
		Print("\n");
		n := n + 1;
	    od;
	fi;
    else
	fd := FileOpenForRead("UULIB:NewsGroups");
	if fd = 0 then
	    Print("This MUD is not set up to offer news reading.\n");
	else
	    if lt = nil then
		lt := CreateThingList();
		me@p_pNGroupList := lt;
	    fi;
	    while
		line := FileRead(fd);
		if line = "" then
		    false
		else
		    n := Index(line, " ");
		    if n < 0 then
			n := Index(line, "\t");
		    fi;
		    if n > 0 then
			line := SubString(line, 0, n);
		    fi;
		    not line == group
		fi
	    do
	    od;
	    FileClose(fd);
	    if line = "" then
		Print(
		    "This MUD does not have group \"" + group + "\".\n"
		    "You probably need to enclose the group name in quotes.\n"
		);
	    elif FindName(me@p_pNGroupList, p_NGroupName, group) ~= fail then
		Print("You already subscribe to group \"" + group + "\".\n");
	    else
		th := CreateThing(nil);
		th@p_NGroupName := group;
		th@p_NHighestRead := 0;
		th@p_NUnread := CreateIntList();
		th@p_NLowest := 0;
		th@p_NHighest := 0;
		AddTail(lt, th);
		Print("Subscribed to group \"" + group + "\".\n");
	    fi;
	fi;
    fi;
    true
corp;

Verb1(g_news, "subscribe", 0, nv_subscribe).
Synonym(g_news, "subscribe", "sub").

define tp_news proc nv_unsubscribe(string name)bool:
    thing me, group;
    list thing lt;

    if name = "" then
	Print("You must specify a group to unsubscribe from.\n");
	false
    else
	me := Me();
	lt := me@p_pNGroupList;
	if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
	then
	    Print("You are not subscribed to group \"" + name + "\".\n");
	else
	    group := FindResult();
	    DelElement(lt, group);
	    ClearThing(group);
	    Print("Unsubscribed from group \"" + name + "\".\n");
	fi;
	true
    fi
corp;

Verb1(g_news, "unsubscribe", 0, nv_unsubscribe).
Synonym(g_news, "unsubscribe", "unsub").

define tp_news proc openDotNextFile(string name)int:
    int i;
    string path;

    path := name;
    while
	i := Index(path, ".");
	i ~= -1
    do
	path := StringReplace(path, i, "/");
    od;
    i := FileOpenForRead("UUNEWS:" + path + "/.next");
    if i ~= 0 then
	/* Grrr. This was not needed in older UUCP stuff! Note that it WILL
	   work for multiple simultaneous readers, since this whole set of
	   stuff is done atomically for a given client. */
	FileClose(i);
	i := FileOpenForWrite("t:crfile");
	FileWrite(i, "\n");
	FileClose(i);
	Execute("delete t:.next");
	Execute("join UUNEWS:" + path + "/.next t:crfile as t:.next");
	i := FileOpenForRead("t:.next");
    fi;
    i
corp;

define tp_news proc openArticleFile(string name; int article)int:
    int i;
    string path;

    path := name;
    while
	i := Index(path, ".");
	i ~= -1
    do
	path := StringReplace(path, i, "/");
    od;
    FileOpenForRead("UUNEWS:" + path + "/" + IntToString(article))
corp;

define tp_news proc nv_catchUp(string name)bool:
    thing me, group;
    list thing lt;
    int fd, nextArticle;
    string line;

    if name = "" then
	Print("You must specify a group to catchup in.\n");
	false
    else
	me := Me();
	lt := me@p_pNGroupList;
	if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
	then
	    Print("You are not subscribed to group \"" + name + "\".\n");
	else
	    group := FindResult();
	    fd := openDotNextFile(name);
	    if fd = 0 then
		Print("No such newsgroup???\n");
	    else
		line := FileRead(fd);
		FileClose(fd);
		nextArticle := StringToPosInt(line);
		if nextArticle <= 0 then
		    Print("Something wrong in that newsgroup!\n");
		elif nextArticle = 1 then
		    Print("No articles to catch up yet!\n");
		else
		    group@p_NHighestRead := nextArticle - 1;
		    Print("Caught up in group \"" + name + "\".\n");
		fi;
	    fi;
	fi;
	true
    fi
corp;

Verb1(g_news, "catchup", 0, nv_catchUp).

define tp_news proc newsFindNextGroup()bool:
    list thing lt;
    thing me, group;
    int which, groupCount, fd, nextArticle, cnt, lo, hi, mid;
    string line, name, prompt;
    list int li;
    bool gotOne;

    me := Me();
    lt := me@p_pNGroupList;
    groupCount := Count(lt);
    gotOne := false;
    which := me@p_pNCurrentGroup;
    while
	if which >= groupCount then
	    false
	else
	    group := lt[which];
	    name := group@p_NGroupName;
	    fd := openDotNextFile(name);
	    if fd = 0 then
		nextArticle := 0;
	    else
		line := FileRead(fd);
		FileClose(fd);
		nextArticle := StringToPosInt(line);
	    fi;
	    if nextArticle <= 1 then
		true
	    else
		lo := group@p_NHighestRead;
		hi := nextArticle - 1;
		while lo < hi do
		    mid := (lo + hi) / 2;
		    fd := openArticleFile(name, mid);
		    if fd = 0 then
			lo := mid + 1;
		    else
			FileClose(fd);
			hi := mid - 1;
		    fi;
		od;
		/* sometimes miss by one */
		fd := openArticleFile(name, lo);
		if fd = 0 then
		    lo := lo + 1;
		else
		    FileClose(fd);
		fi;
		group@p_NLowest := lo;
		lo := lo - 1;
		if group@p_NHighestRead < lo then
		    group@p_NHighestRead := lo;
		fi;
		hi := nextArticle - 1;
		group@p_NHighest := hi;
		lo := group@p_NHighestRead;
		prompt := "Group " + name + " has ";
		if lo < hi then
		    gotOne := true;
		    prompt := prompt + IntToString(hi - lo) + " new";
		fi;
		li := group@p_NUnread;
		cnt := Count(li);
		mid := 0;
		lo := 0;
		hi := 0;
		while hi < cnt do
		    mid := li[hi];
		    fd := openArticleFile(name, mid);
		    if fd ~= 0 then
			FileClose(fd);
			lo := lo + 1;
		    fi;
		    hi := hi + 1;
		od;
		if lo ~= 0 then
		    if not gotOne then
			gotOne := true;
		    else
			prompt := prompt + " and ";
		    fi;
		    prompt := prompt + IntToString(lo) + " old";
		fi;
		if gotOne then
		    Print(prompt + " unread articles.\n");
		    false
		else
		    true
		fi
	    fi
	fi
    do
	which := which + 1;
    od;
    me@p_pNCurrentGroup := which;
    gotOne
corp;

define tp_news proc newsAllDone()void:

    ignore SetPrompt("newsroom> ");
    ignore SetCharacterInputAction(NewsThing@p_vNewsParse);
corp;

define tp_news proc newsReadGroup()void:

    Me()@p_pNUnreadIndex := 0;
    ignore SetPrompt("Rnq? ");
    ignore SetCharacterInputAction(NewsThing@p_vNewsGroupParse);
corp;

define tp_news proc newsNextGroup()void:

    Me()@p_pNCurrentGroup := Me()@p_pNCurrentGroup + 1;
    if newsFindNextGroup() then
	newsReadGroup();
    else
	newsAllDone();
    fi;
corp;

define tp_news proc newsFindNextArticle()bool:
    thing me, group;
    int article;
    list int unread;
    string line;

    me := Me();
    group := me@p_pNGroupList[me@p_pNCurrentGroup];
    article := me@p_pNUnreadIndex;
    unread := group@p_NUnread;
    if article >= Count(unread) then
	article := group@p_NHighestRead + 1;
	if article > group@p_NHighest then
	    false
	else
	    me@p_pNCurrentArticle := article;
	    true
	fi
    else
	me@p_pNCurrentArticle := unread[article];
	true
    fi
corp;

define tp_news proc newsShowHeader()bool:
    thing me;
    int article, fd, colon;
    string line, header;

    me := Me();
    fd := openArticleFile(
	    me@p_pNGroupList[me@p_pNCurrentGroup]@p_NGroupName,
	    me@p_pNCurrentArticle);
    if fd ~= 0 then
	while
	    line := FileRead(fd);
	    if line = "" then
		false
	    else
		colon := Index(line, ":");
		if colon <= 0 then
		    false
		else
		    header := SubString(line, 0, colon);
		    Index(header, " ") < 0
		fi
	    fi
	do
	    Print(line);
	    Print("\n");
	od;
	me@p_pNFd := fd;
	if line ~= "" then
	    me@p_pNString := line;
	fi;
	true
    else
	false
    fi
corp;

define tp_news proc newsNextArticle()void:
    thing me, group;
    int index;

    me := Me();
    group := me@p_pNGroupList[me@p_pNCurrentGroup];
    if newsFindNextArticle() then
	if newsShowHeader() then
	    ignore SetPrompt("Rnsq? ");
	    ignore SetCharacterInputAction(NewsThing@p_vNewsHeaderParse);
	else
	    Print("Hmm. Article has disappeared!\n");
	fi;
    else
	Print("End of group " + group@p_NGroupName + ".\n");
	newsNextGroup();
    fi;
corp;

define tp_news proc newsDoneArticle()void:
    thing me, group;
    int index;

    me := Me();
    index := me@p_pNUnreadIndex;
    group := me@p_pNGroupList[me@p_pNCurrentGroup];
    if index < Count(group@p_NUnread) then
	RemHead(group@p_NUnread);
    else
	group@p_NHighestRead := group@p_NHighestRead + 1;
    fi;
    newsNextArticle();
corp;

define tp_news proc newsFileClose()void:
    int fd;

    fd := Me()@p_pNFd;
    if fd ~= 0 then
	FileClose(fd);
	Me()@p_pNFd := 0;
    fi;
corp;

define tp_news proc newsUnreadArticle()void:
    thing me, group;

    me := Me();
    group := me@p_pNGroupList[me@p_pNCurrentGroup];
    if me@p_pNUnreadIndex >= Count(group@p_NUnread) then
	AddTail(group@p_NUnread, me@p_pNCurrentArticle);
	me@p_pNUnreadIndex := me@p_pNUnreadIndex + 1;
	group@p_NHighestRead := group@p_NHighestRead + 1;
    fi;
    Print("Marking article as unread.\n");
    newsNextArticle();
corp;

define tp_news proc newsEndParse(string line)void:

    if line = "" or line == "n" then
	newsDoneArticle();
    elif line == "s" then
	newsUnreadArticle();
    elif line == "q" then
	newsNextGroup();
    else
	Print("Options are:\n"
		"  n - go on to next article\n"
		"  s - mark article as unread\n"
		"  q - exit from this group\n"
		"Empty line is equivalent to 'n'\n"
	);
    fi;
corp;

define tp_news proc newsShowPage()bool:
    thing me;
    int fd, length, n, width, len;
    string line;

    me := Me();
    fd := me@p_pNFd;
    length := TextHeight(0);
    width := TextWidth(0);
    line := me@p_pNString;
    me -- p_pNString;
    n := 1;
    while
	if n >= length - 1 then
	    false
	else
	    if line = "" then
		line := FileRead(fd);
	    fi;
	    if line = "" then
		FileClose(fd);
		me@p_pNFd := 0;
		ignore SetPrompt("Nsq? ");
		ignore SetCharacterInputAction(newsEndParse);
		false
	    else
		true
	    fi
	fi
    do
	Print(line);
	Print("\n");
	len := Length(line);
	if len <= width then
	    n := n + 1;
	else
	    n := n + (len + width - 4) / (width - 9);
	fi;
	line := "";
    od;
    line ~= ""
corp;

define tp_news proc newsBodyParse(string line)void:

    if line = "" or line == "c" then
	ignore newsShowPage();
    elif line == "n" then
	newsFileClose();
	newsDoneArticle();
    elif line == "s" then
	newsFileClose();
	newsUnreadArticle();
    elif line == "q" then
	newsFileClose();
	newsNextGroup();
    else
	Print("Options are:\n"
		"  c - continue with article\n"
		"  n - go on to next article\n"
		"  s - mark article as unread\n"
		"  q - exit from this group\n"
		"Empty line is equivalent to 'c'\n"
	);
    fi;
corp;

define tp_news proc newsHeaderParse(string line)void:

    if line = "" or line == "r" or line == "y" then
	if newsShowPage() then
	    ignore SetCharacterInputAction(newsBodyParse);
	    ignore SetPrompt("[M O R E] Cnsq? ");
	fi;
    elif line == "n" then
	newsFileClose();
	newsDoneArticle();
    elif line == "s" then
	newsFileClose();
	newsUnreadArticle();
    elif line == "q" then
	newsFileClose();
	newsNextGroup();
    else
	Print("Options are:\n"
		"  r - read the body of this article\n"
		"  n - go on to next article\n"
		"  s - mark article as unread\n"
		"  q - exit from this group\n"
		"Empty line is equivalent to 'r'\n"
	);
    fi;
corp;

define tp_news proc newsGroupParse(string line)void:

    if line = "" or line == "r" then
	newsNextArticle();
    elif line == "n" then
	newsNextGroup();
    elif line == "q" then
	newsAllDone();
    else
	Print("Options are:\n"
	    "  r - read this group\n"
	    "  n - go to next group with unread articles\n"
	    "  q - exit to top level\n"
	    "Empty line is equivalent to 'r'\n"
	);
    fi;
corp;

define tp_news proc nv_read()bool:
    thing me;

    me := Me();
    if me@p_pNGroupList ~= nil then
	me@p_pNCurrentGroup := 0;
	if newsFindNextGroup() then
	    newsReadGroup();
	else
	    Print("No unread news in subscribed-to groups.\n");
	fi;
    else
	Print("You are not subscribed to any groups.\n");
    fi;
    true
corp;

Verb0(g_news, "read", 0, nv_read).

define tp_news proc newsParse(string input)void:
    string word;

    SetTail(input);
    word := GetWord();
    if word ~= "" then
	if FindAnyWord(g_news, word) ~= 0 then
	    ignore Parse(g_news, input);
	else
	    ignore Parse(G, input);
	fi;
    fi;
corp;

NewsThing@p_vNewsParse := newsParse.
NewsThing@p_vNewsGroupParse := newsGroupParse.
NewsThing@p_vNewsHeaderParse := newsHeaderParse.
NewsThing@p_vNewsBodyParse := newsBodyParse.

define tp_news proc newsPostArticle(string s)void:
    int fd;
    thing me;

    me := Me();
    if s = "" then
	Print("Empty article - not posted.\n");
    else
	/* note: server runs atomically, so no conflict over the temp file */
	fd := FileOpenForWrite("T:MUD.article");
	if fd = 0 then
	    Print("Sorry - can't open article file.\n");
	else
	    /* you might be tempted to put a 'Run' in front of this command,
	       so that the player is not hung waiting for an automatic
	       'batchnews' run. Beware, however, since doing so might allow
	       'batchnews' to be run twice at the same time, with the
	       resultant news duplication. */
	    Print("Posting article (could take a while) ...\n");
	    OPrint(""); 	/* force a flush */
	    Log(me@p_pName + " posting article to " + me@p_pNString2 + "\n");
	    FileWrite(fd, "Newsgroups: " + me@p_pNString2 + "\n");
	    me -- p_pNString2;
	    if me@p_pNSubject ~= "" then
		FileWrite(fd, "Subject: " + me@p_pNSubject + "\n");
		me -- p_pNSubject;
	    fi;
	    FileWrite(fd, "X-NewsSoftware: AmigaMUD newsroom\n");
	    FileWrite(fd, "\n");
	    FileWrite(fd, s);
	    FileClose(fd);
	    Execute("postnews < T:MUD.article -f " + FormatName(me@p_pName) +
		    " -r \"" + me@p_pNRealName + "\""
		);
	    Execute("delete T:MUD.article");
	    Print("... done!\n");
	fi;
    fi;
corp;

define tp_news proc nv_post()bool:
    int fd, blank;
    string groupName, line;

    groupName := GetWord();
    if groupName = "" then
	Print("You must specify the name of the newsgroup to post to.\n");
	false
    else
	fd := FileOpenForRead("UULIB:NewsGroups");
	if fd = 0 then
	    Print("This MUD is not set up to offer news reading.\n");
	    false
	elif Me()@p_pNRealName = "" then
	    Print("You cannot post until you have a realname set up.\n");
	    false
	else
	    while
		line := FileRead(fd);
		if line = "" then
		    false
		else
		    blank := Index(line, " ");
		    if blank < 0 then
			blank := Index(line, "\t");
		    fi;
		    if blank > 0 then
			line := SubString(line, 0, blank);
		    fi;
		    not line == groupName
		fi
	    do
	    od;
	    FileClose(fd);
	    if line = "" then
		Print(
		    "This MUD does not have group \"" + groupName + "\".\n"
		    "You probably need to enclose the group name in quotes.\n"
		);
		false
	    else
		Me()@p_pNString2 := groupName;
		groupName := GetTail();
		if groupName ~= "" then
		    if SubString(groupName, 0, 1) = "\"" then
			groupName :=
			    SubString(groupName, 1, Length(groupName) - 2);
		    fi;
		    Me()@p_pNSubject := groupName;
		fi;
		GetDocument("post> ", "Enter news article", "",
			    newsPostArticle, true)
	    fi
	fi
    fi
corp;

VerbTail(g_news, "post", nv_post).

define tp_news proc nv_name()bool:
    thing me;
    string newName;

    if CharacterThing(Character("SysAdmin"))@p_pCanChangeName then
	me := Me();
	newName := GetTail();
	if newName = "" then
	    Print(
		"To set up a real name for use with mail and news, use, e.g.\n"
		"   name \"John H. Smith\"\n");
	else
	    if Index(newName, "\"") = 0 then
		newName := SubString(newName, 1, Length(newName) - 2);
	    fi;
	    me@p_pNRealName := newName;
	    Print(
		"Your 'real name' is now '" + newName + "'. "
		"This will be used on news articles you post and on letters "
		"that you mail.\n");
	fi;
	true
    else
	Print(
	    "The sysadmin of this MUD has not allowed you to set your own "
	    "realname. Send a MUD mail message ('write to SysAdmin') to "
	    "him/her with your full name so that it can be set up. You will "
	    "need a pen and a pad (available from the store in the minimall) "
	    "to be able to do this.\n");
	false
    fi
corp;

VerbTail(g_news, "name", nv_name).
Synonym(g_news, "name", "realname").

define tp_news proc newsIdleHandler()void:
    action a;

    newsFileClose();
    newsAllDone();
    Execute("delete t:crfile t:.next");
    a := Me()@p_pNSaveIdleHandler;
    if a ~= nil then
	call(a, void)();
    fi;
corp;

define tp_news proc newsEnter()status:
    thing me;
    action a;

    me := Me();
    if Character(me@p_pName) = nil then
	/* cannot do this stuff with machines! */
	OPrint(FormatName(me@p_pName) + " will not enter.\n");
	fail
    else
	a := SetCharacterIdleAction(newsIdleHandler);
	if a ~= nil then
	    me@p_pNSaveIdleHandler := a;
	fi;
	a := SetCharacterInputAction(newsParse);
	if a ~= nil then
	    me@p_pNSaveHandler := a;
	fi;
	me@p_pNSavePrompt := SetPrompt("newsroom> ");
	continue
    fi
corp;

define tp_news proc newsExit()status:
    thing me;

    me := Me();
    Execute("delete t:crfile t:.next");
    ignore SetCharacterIdleAction(me@p_pNSaveIdleHandler);
    ignore SetCharacterInputAction(me@p_pNSaveHandler);
    ignore SetPrompt(me@p_pNSavePrompt);
    continue
corp;

define tp_news r_newsRoom CreateThing(r_indoors).
SetupRoom(r_newsRoom, "in the news room",
    "In this room, the normal commands are replaced by another set which "
    "is used to read and post news articles. You can, however, "
    "use 'west' or 'out' to leave this room. Use 'help' to find out how to "
    "use the service provided here.").
r_newsRoom@p_rNoMachines := true.
Connect(r_ne2, r_newsRoom, D_EAST).
Connect(r_ne2, r_newsRoom, D_ENTER).
UniConnect(r_ne2, r_newsRoom, D_UP).
ExtendDesc(r_ne2,
    "The building here is old, but in good repair. Its exterior finish is "
    "that of smooth sandstone blocks, common in important buildings of the "
    "era. A wide staircase leads up to double wooden doors with diamond-paned "
    "windows. The doors are bordered by a pair of sandstone pillars, which "
    "support a similar cap, carved with a pair of lions rampant. At the very "
    "top of the building, you can see granite gargoyles on each corner. A "
    "plaque by the door reads \"Daily News\".").
AddEastChecker(r_ne2, newsEnter, false).
AddEnterChecker(r_ne2, newsEnter, false).
AddUpChecker(r_ne2, newsEnter, false).
AddWestChecker(r_newsRoom, newsExit, false).
AddExitChecker(r_newsRoom, newsExit, false).
Sign(r_ne2, "plaque.door;plaque,by,the", "", "\"Daily News\"").
Scenery(r_ne2,
    "gargoyle;granite."
    "building;old."
    "block;smooth,sandstone."
    "staircase;wide."
    "door;double,wooden."
    "window;diamond-paned,diamond,paned."
    "pillar;sandstone."
    "cap;sandstone."
    "lion;rampant."
    "rampant;lions,lion").

define tp_news proc drawXXXXRoom(string which)void:
    int i;

    GAMove(nil, 16, 24);
    for i from 0 upto 3 do
	GSetPen(nil, i + 1);
	GRectangle(nil, 128 - 2 * i, 52 - 2 * i, false);
	GRMove(nil, 1, 1);
    od;
    GSetPen(nil, C_GOLD);
    GAMove(nil, 28, 42);
    GText(nil, "Read and post");
    GAMove(nil, 36, 52);
    GText(nil, "usenet ");
    GText(nil, which);
    GAMove(nil, 64, 62);
    GText(nil, "here");
corp;

define tp_news NEWSROOM_ID NextEffectId().
define tp_news proc drawNewsRoom()void:

    if not KnowsEffect(nil, NEWSROOM_ID) then
	DefineEffect(nil, NEWSROOM_ID);
	GSetImage(nil, "newsroom");
	IfFound(nil);
	    GShowImage(nil, "", 0, 0, 160, 100, 0, 0);
	Else(nil);
	    drawXXXXRoom("news");
	Fi(nil);
	EndEffect();
    fi;
    CallEffect(nil, NEWSROOM_ID);
corp;

RoomGraphics(r_newsRoom, "NewsRoom", "", NextMapGroup(), 0, 0, drawNewsRoom).

unuse t_streets
